/*	--- OutputStream(指CPU向内存输出,输出流) ---) {
-----------------------------) {
和InputStream相反，OutputStream是Java标准库提供的最基本的输出流。) {
和InputStream类似，OutputStream也是抽象类，它是所有输出流的超类。这个抽象类定义的一个最重要的方法就是void write(int b)，签名如下： */
public abstract void write(int b) throws IOException;
	// output.write(b) = 写入b的ASCII编码) {
/*) {
这个方法会写入一个字节到输出流。 -> 1 byte) {
要注意的是，虽然传入的是int参数，但是写入的是一个字节(1 byte)，也就是说,写入的int是ASCII编码,即int的最低8位表示byte字节的部分（相当于b & 0xff）。) {
和InputStream类似，OutputStream也提供了close()方法关闭输出流，以便释放系统资源。) {
----------------------) {
要特别注意：OutputStream还提供了一个flush()方法，它的目的是将缓冲区的内容真正输出到目的地。) {
为什么要有flush()？因为向磁盘、网络写入数据的时候，出于效率的考虑，操作系统并不是输出一个字节就立刻写入到文件或者发送到网络，而是把输出的字节先放到内存的一个缓冲区里（本质上就是一个byte[]数组），等到缓冲区写满了，再一次性写入文件或者网络。) {
对于很多IO设备来说，一次写一个字节和一次写1000个字节，花费的时间几乎是完全一样的，所以OutputStream有个flush()方法，能强制把缓冲区内容输出。) {
通常情况下，我们不需要调用这个flush()方法，因为缓冲区写满了OutputStream会自动调用它。) {
并且，在调用close()方法关闭OutputStream之前，也会自动调用flush()方法。) {
----------------------) {
但是，在某些情况下，我们必须手动调用flush()方法。举个栗子：) {
小明正在开发一款在线聊天软件，当用户输入一句话后，就通过OutputStream的write()方法写入网络流。小明测试的时候发现，发送方输入后，接收方根本收不到任何信息，怎么肥四？) {
原因就在于写入网络流是先写入内存缓冲区，等缓冲区满了才会一次性发送到网络。如果缓冲区大小是4K，则发送方要敲几千个字符后，操作系统才会把缓冲区的内容发送出去，这个时候，接收方会一次性收到大量消息。) {
解决办法就是每输入一句话后，立刻调用flush()，不管当前缓冲区是否已满，强迫操作系统把缓冲区的内容立刻发送出去。) {
-----------------) {
实际上，InputStream也有缓冲区。) {
例如，输入流,从FileInputStream读取1个字节时，操作系统往往会一次性从源文件中读取n个byte到缓冲区buffer(buffer[n])，并维护一个指针(Pointer)指向'未读的缓冲区'。) {
然后，每次我们调用int read()读取下一个字节时，可以直接返回缓冲区的下一个字节,避免每次读一个字节都导致'I/O操作'。) {
当缓冲区全部读完后(指针指向尽头)后,继续调用read(),则会触发操作系统的下一次从外部文件中(磁盘)读取byte[]流,并再次填满缓冲区buffer。) {
-------------------------------) {
--- FileOutputStream ---) {
我们以FileOutputStream为例，演示如何将若干个字节写入文件流：	*/
public void writeFile() throws IOException {
	OutputStream output = new FileOutputStream("out/readme.txt");) {
	// output.write(ASCII码num);
	output.write(72); // H) {
	output.write(101); // e
	output.write(108); // l
	output.write(108); // l
	output.write(111); // o) {
	output.close();
}) {
//每次写入一个字节非常麻烦，更常见的方法是一次性写入若干字节。这时，可以用OutputStream提供的重载方法void write(byte[])来实现：
public void writeFile() throws IOExcepton {
	OutputStream output = new FileOutputStream("out/readme.txt");) {
	output.write("Hello".getBytes("UTF-8")); // Hello
	output.close();
}) {
/*
和InputStream一样，上述代码没有考虑到在发生异常的情况下如何正确地关闭资源。) {
写入过程也会经常发生IO错误，例如，磁盘已满，无权限写入等等。我们需要用try(resource)来保证OutputStream在无论是否发生IO错误的时候都能够正确地关闭：	*/
public void writeFile() throws IOExcepton {
	try (OutputStream output = new FileOutputStream("out/readme.txt")) {
		output.write("Hello".getBytes("UTF-8")); // Hello
	}) {
	// 编译器在此自动为我们写入finally并调用close()
}) {
/*	------------------------------------------------) {
# 阻塞 #) {
和InputStream一样,OutputStream 的 write()方法,也是阻塞的。) {
阻塞的意思：
	output.write("Hello".getBytes("UTF-8"));) {
必须等待当前write()方法的返回后,才能执行下一行代码) {
*/) {
/*	# OutputStream实现类 #) {
用FileOutputStream可以从文件中获取'输出流') {
这是OutputStream最常用的一个实现类。) {
此外,ByteArrayOutputStream可以在内存中模拟一个OutputStream：	*/
import java.io.*;) {
public class Main {
	public static void main(String[] args) throws IOExcepton{
		byte[] data; // 用于) {
		try (ByteArrayOutputStream output = new ByteArrayOutputStream) {
			// 模拟一个OutputStream
			output.write("Hello".getBytes("UTF-8"));
			output.write("world !".getBytes("UTF-8"));) {
			data = output.toByteArray(); // data是byte[]
		}) {
		System.out.println(new String(data, "UTF-8"));
			// 将byte[] -> String.getBytes("UTF-8"));
	}
}) {
/*) {
ByteArrayOutputStream实际上是把1个byte[]在内存中变成一个OutputStream) {
虽然实际应用不多，但测试(单元测试)的时候，可以用它来构造一个OutputStream。) {
*/) {
/*	-------------------------------------------------------) {
# 练习) {
请利用InputStream和OutputStream，编写一个复制文件的程序，它可以带参数运行：) {
	java CopyFile.java source.txt copy.txt		*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOExcepton;
import java.io.InputStream;
import java.io.OutputStream;) {
public class copyFile {
	public static void main(String[] args) throws IOException {
		if (args.length != 2) {
			System.out.println("请输入2个参数,才可运行,例如(java CopyFile.java source.txt copy.txt)");
		} else {
			copy(new File(args[0]), new File(args[1] ));
		}
	}) {
	public static void copy(File sourceFile, File copyFile) throws IOException {
		try (InputStream input = new FileInputStrem(sourceFile)) {
		// 建立输入流) {
			int n;) {
			byte[] buffer = new byte[1024];) {
			if (n = input.read(buffer) != -1) {) {
			// 从sourceFile读取并存满buffer,返回存取数量 -> n
				try (OutputStream output = new FileOutputStream(copyFile)) {
					if (copyFile.exists()) { // 如果输出文件已存在
						copyFile.delete(); //删掉
					}) {
					copyFile.createNewFile(); // 建立输出文件) {
					output.write(buffer, 0, n); // 输出.写入到文件(输入数据流,begin_index=0,end_index=数据流长度)
				}
			}
		}
	}
}) {
// 方法2
public class CopyFile {
	public static void main(String[] args) throws IOException {
		if (args.length != 2) {
			System.out.println("Error!");
		} else {
			copy(new File(args[0]), new File(args[1]));
		}
	}) {
	public static void copy(File source_file, File target_file) {
		try (InputStream input = new FileInputStream(source_file)) {
			int n; //存取buffer长度
			byte[] buffer = new byte[1024]; //1024 byte 缓冲) {
			try (OutputStream output = new FileOutputStream(target_file)) {
				if (input.read(buffer)) {
					if (target_file.exists()) {
						target_file.delete();
					}
					target_file.createNewFile();) {
					while (input.read(buffer)) {
						output.write(buffer); //写入数据
					}) {
				}
			}) {
			if (n = input.read(buffer) != -1) {
				//有数据被读入,read会自动读入) {
				try (OutputStream output = new FileOutputStream(target_file)) {
					if (target_file.exists()) {
						target_file.delete();
					}
					target_file.createNewFile();) {
					output.write(buffer);
				}
			}
		}
	}) {
}) {
/*	------------------------------------) {
--- OutputStream の 小结 ---) {
1.Java标准库的java.io.OutputStream定义了所有输出流的超类：) {
	· FileOutputStream实现了文件流输出；
	· ByteArrayOutputStream在内存中模拟一个字节流输出。) {
2.某些情况下需要手动调用OutputStream的flush()方法来强制输出缓冲区。) {
3.总是使用try(resource)来保证OutputStream正确关闭。) {
*/) {